{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Analysis of Interferometric Wavefront Data\n", "\n", "In this example, we will see how to use prysm to almost entirely supplant the software that comes with a commerical interferometer to analyze the wavefront of an optic. We begin by importing the relevant classes and setting some aesthetics for matplotlib." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm import Interferogram, FringeZernike, sample_files, zernikefit\n", "\n", "from matplotlib import pyplot as plt\n", "plt.style.use('bmh')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We point prysm to the file, create a new interferogram, mask it to a circular region 100 mm across, subtract piston, tip/tilt and power, and evalute the PV and RMS wavefront error. We also plot the wavefront." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p = sample_files('dat') # sample Zygo .dat file, will be downloaded on demand and saved locally\n", "i = Interferogram.from_zygo_dat(p)\n", "i.crop().mask('circle', 40).crop()\n", "i.remove_piston_tiptilt_power()\n", "print(i.pv, i.rms)\n", "i.plot2d(clim=100, interpolation='bilinear') # +/- 100 nm\n", "plt.grid(False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The interferogram is cropped twice – once to enclose the valid data, then again to apply a mask centered on that region. For relatively conventional interferometry, you may want to stop here. If you want to use a different unit, that is easy enough," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i.change_z_unit('waves')\n", "1/i.pv, 1/i.rms # print reciprocal -- \"one over xxx waves\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is no need to crop again since the outer bound has not changed. Perhaps you wish to evaluated the RMS within the 1 - 10 mm spatial periods," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "i.change_z_unit('nm')\n", "i.fill()\n", "i.bandlimited_rms(1,10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This value is derived from the PSD, so you must call fill first. Do not worry about the corners of the array containing data - it will be windowed out. If you do this on a part which has a central obscuration or otherwise departs from being a circle or rectangle, the result will be correct.\n", "\n", "If you wish to decompose the wavefront into Zernike polynomials, that is easy enough." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# do this on data which has not been filled to avoid errors introduced by the fill value.\n", "coefficients = zernikefit(i.phase, terms=36, norm=True, map_='Fringe')\n", "fz = FringeZernike(coefficients, dia=i.diameter, z_unit=i.z_unit, norm=True)\n", "print(fz)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This print might be a bit daunting, one may prefer to see the top few terms by magnitude," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz.top_n(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or a barplot of all terms," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz.barplot_magnitudes(orientation='v', sort=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The sample data has a circular clear aperture, but if it had a central obscuration (such as transmitted wavefront data for a telescope) that would be easy to mask too. Here we will build a composite mask for the data as if it were a telescope with an annual aperture disrupted by a spider:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.geometry import circle, inverted_circle, generate_spider\n", "\n", "outer = circle(i.samples_x, radius=1) # radius has units of array semidiameter\n", "inner = inverted_circle(i.samples_x, radius=0.35)\n", "\n", "# width has units of arydiam, or pixels if arydiam=None\n", "spider = generate_spider(vanes=3, width=0.5, rotation=90, arydiam=i.diameter, samples=i.samples_x)\n", "mask = outer * inner * spider\n", "\n", "i.mask(mask)\n", "i.plot2d(clim=100) # +/- 100 nm\n", "plt.grid(False)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 2 }